using System;
using System.Collections.Generic;
using System.Text;

using org.synthful.Number;
using org.synthful.NativeInterfaces;

namespace org.synthful.Profiles
{
    /// <summary>
    /// Offered under Apache Public Licence 2.0
    /// synthful.org
    /// blessedgeek [@] synthful.org
    /// </summary>
    public class IniFile
    {
        public IniFile()
        {
        }

        public IniFile(string iniPath)
        {
            _Path = iniPath;
            IniReadSections();
        }

        private Dictionary<string, Dictionary<string, string>> IniBuffer =
            new Dictionary<string, Dictionary<string, string>>();

        static private char[]
            IniRecordDelim = new char[] { (char)10, (char)13 },
            IniParamValueDelim = new char[] { '=' },
            IniValueDelim = { ',', ';' };

        public string Path
        {
            get { return _Path; }
            //set { _Path = value; }
        }
        private string _Path;

        public void IniWriteValue(string section, string key, string value)
        {
            SystemProfile.WritePrivateProfileString(section, key, value, _Path);
            IniPutTransientValue(section, key, value);
        }

        public string IniReadValue(string section, string key)
        {
            return IniReadValue(section, key, String.Empty);
        }

        /// <summary>
        /// Read each section of the ini file into IniBuffer Dictionary.
        /// 
        /// Reading all of the ini file into a two level dictionary is necessary to allow
        /// unequated parameters to be listed in an ini file.
        /// e.g., why list the record {thisIsAStrangeDevice=true}
        /// when we could list the record simply as {thisIsAStrangeDevice}?
        /// 
        /// Since, GetPrivateProfileString call to kernel32 dll does not discriminate
        /// between a {parameter present without value} and an {absent parameter},
        /// this mechanism is needed.
        /// </summary>
        public void IniReadSections()
        {
            byte[] paramSectionBytes = new byte[32767];
            int n = SystemProfile.GetPrivateProfileSectionNames(paramSectionBytes, 32767, _Path);
            //paramSectionBytes = new byte[n];
            //n = GetPrivateProfileSectionNames(paramSectionBytes, 32767, _Path);
            StringBuilder paramSectionBuf = new StringBuilder(n);
            for (int i = 0; i < n; i++)
            {
                if (paramSectionBytes[i] == 0)
                    paramSectionBytes[i] = 13;
                paramSectionBuf.Append((char)paramSectionBytes[i]);
            }
            string[] paramSection = paramSectionBuf.ToString().Split(IniRecordDelim);

            for (int i = 0; i < paramSection.Length; i++)
            {
                if (paramSection[i] == null || paramSection[i].Length == 0)
                    continue;
                IniReadSection(paramSection[i]);
            }
        }

        /// <summary>
        /// Read a section into a Dictionary child-member of IniBuffer Dictionary.
        /// </summary>
        /// <param name="section"></param>
        /// <returns></returns>
        public Dictionary<string, string> IniReadSection(string section)
        {
            Dictionary<string, string> IniSection = null;
            if (!IniBuffer.TryGetValue(section, out IniSection))
            {
                IniSection = new Dictionary<string, string>();
                IniBuffer.Add(section, IniSection);

                byte[] paramSectionBytes = new byte[32767];
                int n =
                    SystemProfile.GetPrivateProfileSection(
                        section, paramSectionBytes, 32767, _Path);
                //paramSectionBytes = new byte[n];
                //n = GetPrivateProfileSection(section, paramSectionBytes, 32767, _Path);
                StringBuilder paramSectionBuf = new StringBuilder(n);
                for (int i = 0; i < n; i++)
                {
                    if (paramSectionBytes[i] == 0)
                        paramSectionBytes[i] = 13;
                    paramSectionBuf.Append((char)paramSectionBytes[i]);
                }
                string[] paramSection = paramSectionBuf.ToString().Split(IniRecordDelim);
                for (int i = 0; i < paramSection.Length; i++)
                {
                    if (paramSection[i].StartsWith("#") || paramSection[i].StartsWith(";"))
                        continue;
                    string[] param = paramSection[i].Split(IniParamValueDelim, 2);
                    if (param == null || param.Length == 0 || param[0].Length == 0) continue;
                    if (IniSection.ContainsKey(param[0])) continue;
                    string value = param.Length > 1 ? param[1] : String.Empty;
                    IniSection.Add(param[0], value);
                }
            }

            return IniSection;
        }

        public string IniReadValue(string section, string key, string defaultValue)
        {
            Dictionary<string, string> IniSection = null;
            if (!IniBuffer.TryGetValue(section, out IniSection))
                return defaultValue;

            string value = null;
            if (!IniSection.TryGetValue(key, out value))
                return defaultValue;

            return value;
        }

        /// <summary>
        /// Write to Ini structure without saving to file. 
        /// </summary>
        /// <param name="section"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool IniPutTransientValue(string section, string key, string value)
        {
            Dictionary<string, string> IniSection = null;
            try
            {
                if (!IniBuffer.TryGetValue(section, out IniSection))
                {
                    IniSection = new Dictionary<string, string>();
                    IniBuffer.Add(section, IniSection);
                }
                IniSection.Add(key, value);
                return true;
            }
            catch
            {
                return false;
            }

        }

        public string[] IniReadValues(string section, string key)
        {
            return IniReadValue(section, key).Split(IniValueDelim);
        }

        public string[] IniReadValues(string section, string key, char[] delimiters)
        {
            return IniReadValue(section, key).Split(delimiters);
        }

        public byte IniReadByteValue(
            string iniSection, string iniKey, byte defaultValue)
        {
            try
            {
                return IniReadByteValue(iniSection, iniKey);
            }
            catch
            {
            }
            return defaultValue;
        }

        public short IniReadShortValue(
            string iniSection, string iniKey, short defaultValue)
        {
            try
            {
                return IniReadShortValue(iniSection, iniKey);
            }
            catch
            {
            }
            return defaultValue;
        }

        public int IniReadIntValue(
            string iniSection, string iniKey, int defaultValue)
        {
            try
            {
                return IniReadIntValue(iniSection, iniKey);
            }
            catch
            {
            }
            return defaultValue;
        }

        public double IniReadDoubleValue(
            string iniSection, string iniKey, double defaultValue)
        {
            try
            {
                return IniReadDoubleValue(iniSection, iniKey);
            }
            catch
            {
            }
            return defaultValue;
        }

        /// <summary>
        /// Different from IniReadIntValue to allow returning a null value to
        /// signify failure.
        /// </summary>
        /// <param name="iniSection"></param>
        /// <param name="iniKey"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public IntegerValue IniReadIntegerValue(
            string iniSection, string iniKey, IntegerValue defaultValue)
        {
            try
            {
                return IniReadIntValue(iniSection, iniKey);
            }
            catch
            {
            }
            return defaultValue;
        }

        public byte IniReadByteValue(
            string iniSection, string iniKey)
        {
            string s = IniReadValue(iniSection, iniKey);
            return Convert.ToByte(s);
        }

        public short IniReadShortValue(
            string iniSection, string iniKey)
        {
            string s = IniReadValue(iniSection, iniKey);
            return Convert.ToInt16(s);
        }

        public int IniReadIntValue(
            string iniSection, string iniKey)
        {
            string s = IniReadValue(iniSection, iniKey);
            return Convert.ToInt32(s);
        }

        public double IniReadDoubleValue(
            string iniSection, string iniKey)
        {
            string s = IniReadValue(iniSection, iniKey);
            return Convert.ToDouble(s);
        }

        public bool IniReadOnOffValue(
            string iniSection, string iniKey, bool defaultValue)
        {
            string s = IniReadValue(iniSection, iniKey, null);
            if (s == null)
                return defaultValue;

            s = s.Trim().ToLower();

            if (s.Equals("on") ||
                s.Equals("1") ||
                s.Equals(""))
                return true;

            if (s.Equals("off") ||
                s.Equals("0"))
                return false;

            return defaultValue;
        }

        public bool IniReadBoolValue(
            string iniSection, string iniKey, bool defaultValue)
        {
            string s = IniReadValue(iniSection, iniKey, null);
            if (s == null)
                return defaultValue;

            s = s.Trim().ToLower();

            if (s.Equals("true") ||
                s.Equals("yes") ||
                s.Equals("1") ||
                s.Equals(""))
                return true;

            if (s.Equals("false") ||
                s.Equals("no") ||
                s.Equals("0"))
                return false;

            return defaultValue;
        }
    }
}
